#include <stdio.h>
#include <stdlib.h>

/*
变量的三要素：
    数据类型
    名字
    存放变量的内存地址

1.基本数据类型
    char 字符型·
    int 整数型
    float 浮点数型（又称为单精度数）
    double 双精度浮点数型

C语言定义的存储类型有4种：auto、extern、static和register，
分别称为自动型、外部型、静态型和寄存器型。
自动型变量可以省略关键字auto。
存储类型在类型之前，即
    存储类型 类型

2.变量的名字和变量声明
    对一个可读性好的程序，必须选择恰当的标识符，取名应统一规范，以便使读者能一目了然。
    声明由类型和具有该类型的变量列表组成。

    变量的存储类型在变量声明中指定。变量声明的一般形式为：
        存储类型 类型 变量名列表
        auto int a;
        static float b, c;
        extern double x;
        register int i=0;
        extern char szClassame[ ];
        static int size=50;
        const double PI=3.14159;

如没有明显地进行初始化，则C编译程序对变量的初始化规则是：
    1）外部型和静态型变量初始值为0。
    2）自动型和寄存器型变量初始值为随机数。

3.变量的地址
    内存地址由系统分配，不同机器为变量分配的地址大小虽然可以不一样，但都必须给它分配地址。
    在C语言中，声明和定义两个概念是有区别的。
        定性：声明是对一个变量的性质（如构成它的数据类型）加以说明，并不为其分配存储空间；
        定量：而定义则是既说明一个变量的性质，又为其分配存储空间。


在C语言中，左值是一个具体的变量，右值一定是一个具体类型的值

变量的操作：
    既可以通过名字对变量进行操作，
    也可以通过地址对存放在该地址的变量进行操作。


a[0]是数组的第1个元素的变量名，所以a[0]可以作为左值。
    即可以使用数组的具体元素作为左值和右值。
    a不是字符串的单个元素，所以都不能作为右值。
    而因为a可以作为数组首地址的值赋给指针变量，所以在这种情况下它又可以作为右值。

一个变量具   有地址   和   值。
    C语言使用地址运算符“&”来取变量存储在内存中的首地址。
        “&a”代表地址，
    C语言提供了“*”运算符，用来取出地址里的值。
        显然“*&a”可以取出值。

4.声明指针类型的变量
    声明指针变量是用普通数据类型加“*”号
        int* p; // 整型类型指针
        char* pc; // 字符类型指针
        float* pf; // 浮点类型指针
        double* pd; // 双精度类型指针
        struct* ps; // 结构类型指针
    系统不管是何种数据类型的指针，一律分配4字节，即各种类型的指针所占内存的大小是一样的。

*/

int main()
{
    int a = 65;
    int addr;
    addr = (int)&a; // 将a的首地址存入变量addr
    /*
    int * addr; // 指针类型存储的是地址值。
    addr = &a; // 这里使用的地址值是另外一个变量的地址，所以是有效地址。

要明确的是，地址值不一定是有效地址，
所以说从指针的引入开始，也就暗示着它存在着无法预防的错误。
    */

    printf("0x%p, 0x%p, 0x%p\n", &a, addr, &addr); // 输出3个地址
    printf("%d, %d, 0x%p\n", a, *&a, *&addr);      // 输出变量及地址里的值
                                                   /*
                                                       &a虽然和addr的值是一个值，但它们对运算的反应并不一样。
                                                       编译系统并不知道*addr存储的“0x0012ff7c”是地址，所以将它作为整数，
                                                       因此编译系统会报错，当然使用**addr也要出错。
                                                       但addr里面确实装的是地址，所以可以将这个整数强制转为地址。*addr加上强制转换
                                                       “（int*）addr”，它的内容是变量a的地址
                                                       再对它使用*运算符，即*（int*）addr，输出结果应该是存在这个地址里的变量a的值
                                                   */
    printf("convert type...\n");
    printf("0x%p, 0x%p, 0x%p\n", &a, addr, (int *)addr);
    printf("%d,%d,%d\n", a, *&a, *(int *)addr);
    system("pause");
}